#ifndef __UDP_CLIENT_HPP__
#define __UDP_CLIENT_HPP__

#include "log.hpp"
#include <string.h>
#include <string>
#include <memory>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace LogModule;

#define Die(code)   \
    do              \
    {               \
        exit(code); \
    } while (0)

static int defaultfd = -1;

namespace UdpClientModule
{

    class UdpClient
    {
    public:
        UdpClient(const std::string &ip, const uint16_t port)
            : _sockfd(defaultfd),
              _ip(ip),
              _port(port)
        {
        }
        ~UdpClient()
        {
        }

        void InitClient()
        {
            // 1.创建一个socket
            _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
            // 这里我们使用了C标准库的socket函数来创建一个UDP socket
            // 注意：在实际的代码中，我们需要检查socket函数的返回值，以确保
            // socket创建成功。

            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "Failed to create socket" << strerror(errno);
                // 如果socket创建失败，我们记录一条FATAL级别的日志，并返回。
                Die(3);
            }

            // 如果socket创建成功，我们记录一条INFO级别的日志，表示socket创建成功。
            LOG(LogLevel::INFO) << "Socket created successfully, sockfd: " << _sockfd;

            // 2.绑定地址信息

            memset(&_server, 0, sizeof(_server)); // 清空结构体
            // 这里我们使用了bzero函数来清空local结构体，确保没有残留数据,垃圾值
            _server.sin_family = AF_INET;                     // 设置地址族为IPv4
            _server.sin_port = htons(_port);                  // 设置端口号
            _server.sin_addr.s_addr = inet_addr(_ip.c_str()); // 将IP地址转换为网络字节序

            // client必须也要有自己的ip和端口！但是客户端，不需要自己显示的调用bind！！
            // 而是，客户端首次sendto消息的时候，由OS自动进行bind
            // 1. 如何理解client自动随机bind端口号？ 一个端口号，只能被一个进程bind
            // 2. 如何理解server要显示的bind？服务器的端口号，必须稳定！！必须是众所周知且不能改变轻易改变的!
            // 如果服务端改变，那么他所服务对接的众多客户端都无法正常运行
        }
        void Start()
        {

            while (true)
            {
                std::cout << "Please input your message: ";
                std::string message;
                getline(std::cin, message); // 从标准输入读取一行消息

                // 发送消息
                ssize_t m = ::sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&_server, sizeof(_server));

                struct sockaddr_in temp;
                socklen_t len = sizeof(temp);
                char buffer[1024];
                ssize_t n = ::recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&temp), &len);
                if (n > 0)
                {
                    buffer[n] = 0;
                    std::cout << buffer << std::endl;
                }
            }
        }

    private:
        int _sockfd;                // socket文件描述符
        std::string _ip;            // IP地址
        uint16_t _port;             // 端口号
        struct sockaddr_in _server; // 我们的类初始化时必须传入目的地的IP与端口
    };
}

#endif